'''
python -m pip install wmi
python -m pip install pycryptodome
'''
import base64
from ctypes import *
import binascii
import hashlib

import os
import winreg
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

import wmi


def _ZIMU(d1, d2):
    return c_uint32(d1*d2).value
def _ZOO3(d1,  d2):
    d1 = c_uint32(d1)
    d2 = c_uint32(d2)
    return _ZIMU(d2.value, c_uint32(d1.value >> 16).value)
def _ZOO1(d1,  d2,  d3):
    return c_uint32(_ZIMU(d2, d1) - _ZOO3(d1, d3)).value
def _ZOO2(d1,  d2,  d3):
    return c_uint32(_ZIMU(d2, d1) + _ZOO3(d1, d3)).value
def CS64_WordSwap(pSrcStr: list,  iChNum: int,  pdwMd5: list, pOutInt: list) -> bool:
    if iChNum < 2 or iChNum & 1:
        return False
    dwMD5_0 =c_uint32((pdwMd5[0] | 1) + 0x69FB0000) .value
    dwMD5_1 = c_uint32((pdwMd5[1] | 1) + 0x13DB0000).value
    dwRet1 = 0
    dwRet0 = 0
    i=0
    while i<iChNum:
        dwTemp00 = dwRet0
        if (i < iChNum):
            dwTemp00 += pSrcStr[i]
        dwTemp00=c_uint32(dwTemp00).value

        dwTemp01 = _ZOO1(dwTemp00, dwMD5_0, 0x10FA9605)
        dwTemp02 = _ZOO2(dwTemp01, 0x79F8A395, 0x689B6B9F)
        dwTemp03 = _ZOO1(dwTemp02, 0xEA970001, 0x3C101569)
        i+=1

        dwTemp04 = dwTemp03
        if (i < iChNum):
            dwTemp04 += pSrcStr[i]
        dwTemp04=c_uint32(dwTemp04).value

        dwTemp05 = _ZOO1(dwTemp04, dwMD5_1, 0x3CE8EC25)
        dwTemp06 = _ZOO1(dwTemp05, 0x59C3AF2D, 0x2232E0F1)
        dwTemp07 = _ZOO2(dwTemp06, 0x1EC90001, 0x35BD1EC9)
        i+=1

        dwRet0 = dwTemp07
        dwRet1 = c_uint32(dwTemp03 + dwRet0 + dwRet1).value

    pOutInt[0] = dwRet0
    pOutInt[1] = dwRet1
    print('CS64_WordSwap pOutInt:',','.join(map(hex,pOutInt)))
    return True
def CS64_Reversible(pSrcStr: list,  iChNum: int,  pdwMd5: list, pOutInt: list) -> bool:
    if (iChNum < 2 or iChNum & 1):
        return False
    dwRet1 = 0
    dwRet0 = 0
    dwMD5_0 = pdwMd5[0] | 1
    dwMD5_1 = pdwMd5[1] | 1
    i=0
    while i <iChNum:
        dwTemp00 = dwRet0
        if i < iChNum:
            dwTemp00 += pSrcStr[i]
        dwTemp00=c_uint32(dwTemp00).value
        dwTemp01 = c_uint32(dwMD5_0 * dwTemp00).value
        dwTemp02 = _ZOO1(dwTemp01, 0xB1110000, 0x30674EEF)
        dwTemp03 = _ZOO1(dwTemp02, 0x5B9F0000, 0x78F7A461)
        dwTemp04 = _ZOO2(dwTemp03, 0xB96D0000, 0x12CEB96D)
        dwTemp05 = _ZOO2(dwTemp04, 0x1D830000, 0x257E1D83)
        i+=1

        dwTemp06 = dwTemp05
        if (i < iChNum):
            dwTemp06 += pSrcStr[i]
        dwTemp06=c_uint32(dwTemp06).value
        dwTemp07 = c_uint32(dwMD5_1 * dwTemp06).value
        dwTemp08 = _ZOO1(dwTemp07, 0x16F50000, 0x5D8BE90B)
        dwTemp09 = _ZOO1(dwTemp08, 0x96FF0000, 0x2C7C6901)
        dwTemp10 = _ZOO2(dwTemp09, 0x2B890000, 0x7C932B89)
        dwTemp11 = _ZOO1(dwTemp10, 0x9F690000, 0x405B6097)
        i+=1

        dwRet0 = dwTemp11
        dwRet1 = c_uint32(dwTemp05 + dwRet0 + dwRet1).value

    pOutInt[0] = dwRet0
    pOutInt[1] = dwRet1
    print('CS64_Reversible pOutInt:',','.join(map(hex,pOutInt)))
    return True
def BuildPatentHash_(pWsStr:bytes,   pMd5:list, pOut:list):
    dwWsLen=len(pWsStr)
    dwCount = dwWsLen // 4
    if (dwCount & 1):
        dwCount -= 1
    r1 = [0,0]
    r2 = [0,0]
    plist=[]
    for i in range(dwCount):
        plist.append(int.from_bytes(pWsStr[i*4:i*4+4],'little') )
    if (not CS64_WordSwap(plist, dwCount, pMd5, r1) or not CS64_Reversible(plist, dwCount, pMd5, r2)):
        return False
    for i in range(len(r1)):
        pOut.append(r1[i] ^ r2[i])
    return True


def getmd5hex_bs(bs: bytes) -> bytes:
    md5ch = hashlib.md5(bs)
    md5str = md5ch.hexdigest()
    print(md5str)
    return md5ch.digest()

def mteaencrypt(v, k):
    v0, v1 = c_uint32(v[0]), c_uint32(v[1])
    delta = 0x61C88647
    k0, k1, k2, k3 = k[0], k[1], k[2], k[3]

    total = c_uint32(0x9E3779B9)
    for i in range(8):
        # total.value += delta
        v0.value += ((v1.value << 4) + k0) ^ (v1.value + total.value) ^ ((v1.value >> 5) + k1)
        v1.value += ((v0.value << 4) + k2) ^ (v0.value + total.value) ^ ((v0.value >> 5) + k3)
        total.value -= delta
    # print(hex(total.value))
    return v0.value, v1.value
def mteadecrypt(v, k):
    v0, v1 = c_uint32(v[0]), c_uint32(v[1])
    delta = 0x61C88647  # 0x9e3779b9
    k0, k1, k2, k3 = k[0], k[1], k[2], k[3]
    total = c_uint32(0x8ff34781)  # 0x61C88647
    for i in range(8):
        total.value += delta
        v1.value -= ((v0.value << 4) + k2) ^ (v0.value + total.value) ^ ((v0.value >> 5) + k3)
        v0.value -= ((v1.value << 4) + k0) ^ (v1.value + total.value) ^ ((v1.value >> 5) + k1)
    return v0.value, v1.value
def tea360(databs: bytes, size: int = 0x80) -> bytes:  # tes 128
    dsz = len(databs)
    if dsz % 4 != 0 or size % 4 != 0:
        print('len error!')
        return None

    if size > dsz:
        # print('pad to ',size)
        databs += b'\x00'*(size-dsz)
        dsz = size
    idatas = []
    keys = []
    for i in range(0, dsz, 4):
        inum = int.from_bytes(databs[i:i+4], 'little')
        if i < 16:
            keys.append(inum)
        idatas.append(inum)
    bs = b''
    for j in range(0, len(idatas), 2):
        x, y = mteaencrypt(idatas[j:j+2], keys)
        bs += x.to_bytes(4, 'little')
        bs += y.to_bytes(4, 'little')
    return bs

def getUnicode_bs(s:str)->bytes:
    return s.encode('UTF-16LE')
def getMID()->str:
    MachineGuidkey = winreg.OpenKey(
        winreg.HKEY_LOCAL_MACHINE,
        "SOFTWARE\\Microsoft\\Cryptography",
        0,
        winreg.KEY_READ | winreg.KEY_WOW64_64KEY
    )
    MachineGuidstr = winreg.QueryValueEx(MachineGuidkey, "MachineGuid")[0]
    print('MachineGuidstr:',MachineGuidstr)
    return MachineGuidstr


def getSID()->str:
    mwmi=wmi.WMI()
    us=mwmi.Win32_UserAccount()
    for u in us:
        if u.Name==os.getlogin():
            print(u.Name,u.SID)
            return u.SID

def rand(myseed):
    n = myseed * 0x343fd + 0x269ec3
    myseed = n & 0xffffffff
    return myseed
def randenc(data: bytes, seed: int = 0x8000402B) -> bytes:
    out = seed.to_bytes(4, 'little')
    sz = len(data)
    n = sz % 4
    if n:
        m = sz//4*4
    else:
        m = sz
    for i in range(0, m, 4):
        seed = rand(seed)
        bs = seed.to_bytes(4, 'little')
        for j in range(4):
            out += (data[i+j] ^ bs[j]).to_bytes(1, 'little')

    seed = rand(seed)
    for i in range(n):
        bs = seed.to_bytes(4, 'little')
        out += (data[m+i] ^ bs[i]).to_bytes(1, 'little')
    return out

def urlenc(bs:bytes)->bytes:
    ret=b''
    unreservedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.:/"
    for x in bs:
        if chr(x) in unreservedChars:
            ret+=x.to_bytes(1,'little')
        elif chr(x)==' ':
            ret+=b'+'
        else:
            ret+=b'%%%02x'%x

    return ret

#148 (0x94)
stub=[
    0x1F, 0x7D, 0x14, 0x89, 0x4D, 0xB8, 0x8B, 0x4D, 0x18, 0x89, 0x45, 0xBC, 0x89, 0x7D, 0xB0, 0x89,
    0x4D, 0xAC, 0x89, 0x5D, 0xD0, 0x89, 0x5D, 0xC8, 0x88, 0x5D, 0xD7, 0x88, 0x5D, 0xC0, 0x3B, 0xFB,
    0x0F, 0x84, 0x77, 0x93, 0x03, 0x00, 0x89, 0x90, 0x8B, 0x4E, 0x10, 0xF7, 0x41, 0x08, 0x00, 0x40,
    0x00, 0x00, 0x0F, 0x84, 0x6F, 0x93, 0x03, 0x00, 0xF7, 0x46, 0x68, 0x00, 0x01, 0x00, 0x00, 0x0F,
    0x85, 0x58, 0xCC, 0x03, 0x00, 0xF6, 0x05, 0xA0, 0x03, 0xFE, 0x7F, 0x01, 0xBF, 0x00, 0x01, 0x00,
    0x02, 0x0F, 0x85, 0x4F, 0x96, 0x03, 0x00, 0x89, 0x5D, 0xD8, 0x85, 0x7E, 0x68, 0x0F, 0x85, 0x8B,
    0x96, 0x03, 0x00, 0x38, 0x5E, 0x02, 0x0F, 0x85, 0xEA, 0x96, 0xCC, 0x00, 0xEB, 0x1D, 0x39, 0x5D,
    0xC8, 0x0F, 0x85, 0x3B, 0x97, 0x03, 0x00, 0x8B, 0x45, 0xD8, 0x8B, 0x4D, 0xFC, 0x5F, 0x5E, 0x33,
    0x10, 0x00, 0x00, 0x00, 0x57, 0x00, 0x44, 0x00, 0x4C, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00,
    0x36, 0x00, 0x00, 0x00
]
stub=bytes(stub)
ploadone=b''
ploadone+=stub
ploadone+=getUnicode_bs(getSID())
ploadone+=getUnicode_bs('/?')
ploadone+=getUnicode_bs(getMID())
print('ploadone:',binascii.b2a_hex(ploadone))
md5ploadone_bs=getmd5hex_bs(ploadone)

a=int.from_bytes(md5ploadone_bs[:4],'little')
print('a:',hex(a))
b=int.from_bytes(md5ploadone_bs[4:8],'little')
print('b:',hex(b))
imd5=[a,b]
pout=[]
BuildPatentHash_(ploadone,imd5,pout)
mhashbs=pout[0].to_bytes(4, 'little')
mhashbs+=pout[1].to_bytes(4, 'little')

print('pout:',','.join(map(hex,pout)),mhashbs)
mhash_encbs=urlenc(base64.b64encode(mhashbs))
print(mhash_encbs)

ploadtwo=b''
ploadtwo+=int.to_bytes(1,4,'little')
ploadtwo+=int.to_bytes(len(stub),4,'little')
ploadtwo+=stub
ploadtwo+=int.to_bytes(2,4,'little')
ploadtwo+=int.to_bytes(len(mhash_encbs),4,'little')
ploadtwo+=mhash_encbs
print('ploadtwo:',binascii.b2a_hex(ploadtwo))

randenc_bs = randenc(ploadtwo)
print('[#]gen p1')
tmp_bs = getmd5hex_bs(randenc_bs)
tmp_bs = tea360(binascii.b2a_hex(tmp_bs))
tmp_bs = pad(tmp_bs, 0xc0+1, 'x923')[:-1]
print(binascii.b2a_hex(tmp_bs))

print('\n#######################################################################################################')
print('[#]360History sqlite db key:')
key_bs = getmd5hex_bs(tmp_bs)
print('#######################################################################################################\n')

# bookmark
aesch=AES.new(key=binascii.b2a_hex(key_bs),iv=b'33mhsq0uwgzblwdo',mode=AES.MODE_CBC)
bookmark=b''
filePath = 'C:\\Users\\Administrator\\AppData\\Roaming\\secoresdk\\360se6\\User Data\\Default\\360Bookmarks'
# filePath = 'C:\\Users\\Administrator\\AppData\\Roaming\\360Safe\\MultiTip\\Cache\\Default\\360Bookmarks'
with open(filePath,'rb') as f:
    bookmark=f.read()
bs=base64.b64decode(bookmark)[4:]
bs=aesch.decrypt(bs)
print(bs.decode())
with open('bookmark.txt','wb') as f:
    f.write(bs)
